<template>
    <div :id="`image_region_wrapper_${props._region_id}`" class="image-tools-wrapper">
        <div class="image-toolbar-wrapper">
            <!-- 动态加载工具栏组件 -->
            <component :is="toolbarComponent" v-if="toolbarComponent" :_label_mode="_label_mode"
                :_dataset_info="_dataset_info" />
        </div>
        <div class="image-panel">
            <el-tooltip v-if="props._show_name" class="box-item" effect="dark" :content="_image_path"
                placement="bottom-start">
                <div class="image-info">
                    {{ _image_name }}
                </div>
            </el-tooltip>
            <canvas :id="`image_region_canvas_${props._region_id}`"></canvas>
        </div>
    </div>
    <!-- pipeline列表对话框 -->
    <PipeDlg ref="pipelineDialog" @dlg-confirm="onPipelineInput"></PipeDlg>
</template>

<script setup>
import API from '@/js/api';
import LabelConfig from '@/js/models/LabelConfig';
import { ref, onMounted, onUnmounted, provide, inject, computed, defineAsyncComponent } from 'vue';
import { fabric } from 'fabric';
import { cloneDeep } from 'lodash';
import { getFileBaseName } from '@/js/utils/fileUtils';
import { calcInitZoom, getCanvasCoords, limitPointerInImage } from '@/js/utils/imageUtils';
import { startPanViewport, updatePanViewport, stopPanViewport } from '@/js/utils/canvasUtils';
import { generateTimestampBasedID } from '@/js/utils/baseUtils';
import PointTool from '@/js/canvasTools/PointTool';
import RectTool from '@/js/canvasTools/RectTool';
import PolyTool from '@/js/canvasTools/PolyTool';
import PipeDlg from '@/components/PipelinePages/PipeDlg.vue';

provide('vueImageRegion', { clickLabelTool, emitMessage });

let vueApp = inject('vueApp');
const name = 'ImageRegion';
const methods = {
    clickLabelTool: clickLabelTool
};
const props = defineProps(['_region_id', '_readonly', '_toolbar', '_sync_ops', '_show_name', '_highlight']);
const emit = defineEmits(['updateImageInfo', 'deleteLabel', 'selectLabel', 'unselectLabel', 'preLabelData', 'nextLabelData', 'deleteLabelData', 'syncMouseEvent', 'autolabel']);
defineExpose({ loadDataset, loadData, handleKeyDown, deleteLabel, selectLabel, changeLabelMode, invokeMethodByName, autolabel });

// 对应的fabricjs对象和属性
let _canvas = null;
let _image = null;
let _canvas_vtf = null;

// 鼠标绘制线
let _horizontalLine = null;
let _verticalLine = null;

// 工具相关的数据对象
let _label_mode = ref('pointer');
let _label_tools = null;
let _label_result = null;

// dataset_info信息
let _dataset_info = ref();
let _tools_config = ref([]);

// image属性信息
let _pairs_id = ref('');
let _image_path = ref('');
let _image_name = ref('');

// 调用pipeline对话框
const pipelineDialog = ref(null);

// 加载数据集过程中的提示文字
let loading = null;

// 动态加载组件;
const toolbarComponent = computed(() => {
    if (!props._toolbar || props._toolbar === 'none') return null;
    return defineAsyncComponent(async () => {
        let compPath = `./${props._toolbar.supertype}/${props._toolbar.type}/Toolbar.vue`;
        try {
            console.log(`加载组件 ${compPath}`);
            const module = await import(`./${props._toolbar.supertype}/${props._toolbar.type}/Toolbar.vue`);
            return module.default;
        } catch (error) {
            console.error(`组件 ${compPath} 加载失败`, error);
            return null;
        }
    });
});

// 计算图片加载时候的默认放大比例
function initCanvas() {
    // console.log(`the ImageRegion component is now mounted.`)
    let canvas_wrapper = document.getElementById(`image_region_wrapper_${props._region_id}`);
    let canvas_size = {
        width: canvas_wrapper.offsetWidth,
        height: canvas_wrapper.offsetHeight
    };

    _canvas = new fabric.Canvas(`image_region_canvas_${props._region_id}`, {
        backgroundColor: 'gray',
        width: canvas_size.width,
        height: canvas_size.height,
        fireRightClick: true,
        stopContextMenu: true
    });

    // _canvas.defaultCursor = 'crosshair';
    _canvas.selection = false;
    _canvas.on('mouse:down', onMouseDown);
    _canvas.on('mouse:up', onMouseUp);
    _canvas.on('mouse:move', onMouseMove);
    _canvas.on('mouse:wheel', onMouseWheel);
    _canvas.on('selection:cleared', onSelectionCleared);
    _canvas_vtf = cloneDeep(_canvas.viewportTransform);

    // 设置标注工具
    if (props._readonly) {
        _label_tools = {};
    } else {
        _label_tools = {
            point: new PointTool(_canvas),
            rect: new RectTool(_canvas),
            poly: new PolyTool(_canvas)
        };
    }
}

// 更新画布大小
function updateCanvasSize() {
    if (_canvas) {
        let canvas_wrapper = document.getElementById(`image_region_wrapper_${props._region_id}`);
        let canvas_size = {
            width: canvas_wrapper.offsetWidth,
            height: canvas_wrapper.offsetHeight
        };
        // console.log(`updateCanvasSize size = `, canvas_size);
        _canvas.setDimensions(canvas_size);
        _canvas.renderAll();
    }
}

function unsetCanvas() {
    // console.log('unsetCanvas!');
    _canvas.off('mouse:down', onMouseDown);
    _canvas.off('mouse:up', onMouseUp);
    _canvas.off('mouse:move', onMouseMove);
    _canvas.off('mouse:wheel', onMouseWheel);
    _canvas.off('selection:cleared', onSelectionCleared);

    _canvas_vtf = null;
    _canvas = null;
    _label_tools = null;
}

function clearCanvas() {
    // _canvas.discardActiveObject();

    // 清除当前工具的状态
    for (let mode in _label_tools) {
        let tool = _label_tools[mode];
        tool && tool.clear();
    }

    // 清除canvas和image对象
    _canvas.clear();
    _image = null;
    _horizontalLine = null;
    _verticalLine = null;

    // 清楚当前_label_result
    _label_result = null;

    return true;
}

function updateCrossLines(event) {
    if (!_image) {
        return;
    }
    const pointer = _canvas.getPointer(event.e);

    // 调整到图像坐标系进行绘制
    let p0 = { x: 0, y: 0 };
    let p1 = { x: _image.width, y: _image.height };
    const tps = getCanvasCoords(_image, [p0, p1]);
    limitPointerInImage(_image, pointer);

    if (!_horizontalLine) {
        // Create horizontal line
        _horizontalLine = new fabric.Line([tps[0].x, pointer.y, tps[1].x, pointer.y], {
            stroke: 'white',
            strokeWidth: 1,
            selectable: false,
            evented: false,
            strokeDashArray: [5, 5],
            opacity: _label_mode.value == 'pointer' ? 0 : 1
        });
        _canvas.add(_horizontalLine);
    } else {
        // Update horizontal line
        _horizontalLine.set({ y1: pointer.y, y2: pointer.y });
    }

    if (!_verticalLine) {
        // Create vertical line
        _verticalLine = new fabric.Line([pointer.x, tps[0].y, pointer.x, tps[1].y], {
            stroke: 'white',
            strokeWidth: 1,
            selectable: false,
            evented: false,
            strokeDashArray: [5, 5],
            opacity: _label_mode.value == 'pointer' ? 0 : 1
        });
        _canvas.add(_verticalLine);
    } else {
        // Update vertical line
        _verticalLine.set({ x1: pointer.x, x2: pointer.x });
    }

    _canvas.requestRenderAll();
}

// 给各个工具设置对应的image对象，方便判断是否超出图像范围
function setToolsImage() {
    for (let name in _label_tools) {
        let tool = _label_tools[name];
        tool.setImage(_image);
    }
}

async function getImageURL(image_path) {
    // 判断是否在electron环境打开，并获取到图片blob数据
    return API.api_url('LoadImage', { image_path: image_path });
}

// 加载数据集，主要是为了设定工具栏
function loadDataset(dataset_info) {
    // console.log('ImageRegion.vue.loadDataset dataset_info = ', dataset_info);
    _dataset_info.value = dataset_info;
    let config = LabelConfig.getConfig();
    if (config) {
        _tools_config.value = config.tools;
    }
}

// 加载具体的数据
async function loadData(data) {
    // console.log('ImageRegion.vue::loadData data = ', data);
    let image_path = data.image_url;
    _pairs_id.value = data.pairs_id;
    _image_path.value = image_path;
    _image_name.value = getFileBaseName(image_path);
    if (data.hasOwnProperty('label_result')) {
        // 绘制标注结果
        paint(image_path, (canvas, image) => {
            setToolsImage();
            _label_result = data.label_result;
            data.label_result.bindCanvas(canvas, image, _label_tools);
            // 先设置highlight后设置readonly
            // 如果highlight为false且readonly为false，则关闭event
            if (props._highlight) {
                data.label_result.setHighlight();
            }
            if (props._readonly) {
                data.label_result.setEditableMode(false);
            }
        });
    } else if (data.hasOwnProperty('eval_result')) {
        // 绘制评测结果
        paint(image_path, (canvas, image) => {
            data.eval_result.bindCanvas(canvas, image);
        });
    } else if (data.hasOwnProperty('input_result')) {
        console.log('ImageRegion.vue::loadData paint image_path = ', image_path);
        paint(image_path, (canvas, image) => {
            setToolsImage();
            vueApp.monitorKeyEvent(false);
            _tools_config.value = data.input_result.tools;
            data.input_result.bindCanvas(canvas, image, _label_tools);
            _label_mode.value = data.input_result.mode;
        });
    }
}

// 绘制图片，图片绘制成功回调绘制label
async function paint(image_path, paint_cb) {
    // 清空历史加载的内容
    clearCanvas();
    _label_mode.value = 'pointer';

    // 显示加载中，并关闭快捷键响应
    if (!loading) {
        loading = ElLoading.service({
            lock: true,
            text: '图片加载中...',
            background: 'rgba(0, 0, 0, 0.7)'
        });
    } else {
        loading.setText('图片加载中...');
    }
    vueApp.monitorKeyEvent(false);

    // 动态获取到image的url
    let img_url = await getImageURL(image_path);
    if (!img_url) {
        loading && loading.close();
        loading = null;
    }

    // 正式加载图片资源
    let canvas_wrapper = document.getElementById(`image_region_wrapper_${props._region_id}`);
    let canvas_size = {
        width: canvas_wrapper.offsetWidth,
        height: canvas_wrapper.offsetHeight
    };
    _canvas.backgroundColor = '#000';
    _canvas.setDimensions(canvas_size);

    fabric.Image.fromURL(
        img_url,
        (img) => {
            // 关闭加载提示，并响应快捷键
            loading && loading.close();
            loading = null;
            vueApp.monitorKeyEvent(true);

            // 如果是blob格式则释放内存
            if (img_url instanceof Blob) {
                window.URL.revokeObjectURL(img_url);
            }

            // 设置image对象，并添加到canvas中
            _image = img;
            img.set({
                originX: 'center',
                originY: 'center',
                left: canvas_size.width / 2,
                top: canvas_size.height / 2,
                hasControls: false,
                hasBorders: false,
                hoverCursor: 'crosshair',
                selectable: false
            });
            let img_size = {
                width: img.width,
                height: img.height
            };
            _canvas.add(img);

            // 初始化图片位置
            let center_point = {
                x: canvas_size.width / 2,
                y: canvas_size.height / 2
            };
            _canvas.viewportTransform = _canvas_vtf;
            let zoom = calcInitZoom(canvas_size, img_size);
            _canvas.zoomToPoint(center_point, zoom);
            emit('updateImageInfo', img_size);

            // 图片绘制完成后，执行回调
            paint_cb && paint_cb(_canvas, _image);
        },
        { crossOrigin: 'anonymous' }
    );
}

function invokeMethodByName(methodName, args) {
    if (methodName == 'onMouseDown') {
        onMouseDown(args);
    } else if (methodName == 'onMouseUp') {
        onMouseUp(args);
    } else if (methodName == 'onMouseMove') {
        onMouseMove(args);
    } else if (methodName == 'onMouseWheel') {
        onMouseWheel(args);
    }
}

////////////////////////////////////////////////////////////////////////////////////
// canvas mouse event handler
function onMouseDown(event) {
    // console.log('mousedown', event.absolutePointer);
    if (event.button === 3) {
        startPanViewport(_canvas, event);
    } else {
        for (let mode in _label_tools) {
            if (_tools_config.value.includes(mode)) {
                let tool = _label_tools[mode];
                _label_mode.value == mode && tool.onMouseDown(event);
            }
        }
    }

    // 对比查看评测结果，同步鼠标操作信息
    if (props._sync_ops && !event.hasOwnProperty('sync_tag')) {
        emit('syncMouseEvent', {
            method: 'onMouseDown',
            event: event
        });
    }

    // 更新canvas的显示内容
    _canvas.requestRenderAll();
}

function onMouseUp(event) {
    // console.log('mouseup', event.absolutePointer);
    if (event.button === 3) {
        stopPanViewport(_canvas);
    }

    // 同步鼠标操作信息
    if (props._sync_ops && !event.hasOwnProperty('sync_tag')) {
        emit('syncMouseEvent', {
            method: 'onMouseUp',
            event: event
        });
    }

    // 更新canvas的显示内容
    _canvas.requestRenderAll();
}

function onMouseMove(event) {
    // console.log('mousemove', event);
    if (_canvas.isDragging) {
        updatePanViewport(_canvas, event);
    } else {
        for (let mode in _label_tools) {
            if (_tools_config.value.includes(mode)) {
                let tool = _label_tools[mode];
                _label_mode.value == mode && tool.onMouseMove(event);
            }
        }
    }
    updateCrossLines(event);

    // 同步鼠标操作信息
    if (props._sync_ops && !event.hasOwnProperty('sync_tag')) {
        emit('syncMouseEvent', {
            method: 'onMouseMove',
            event: event
        });
    }

    // 更新canvas的显示内容
    _canvas.requestRenderAll();
}

function onMouseWheel(event) {
    // console.log('mousewheel', event);
    // console.log(`deltaX = ${event.e.deltaX}, deltaY = ${event.e.deltaY}`);
    let delta = event.e.deltaY;
    let zoom = _canvas.getZoom();
    zoom *= 0.999 ** delta;
    if (zoom > 20) zoom = 20;
    if (zoom < 0.01) zoom = 0.01;
    let pt = { x: event.e.offsetX, y: event.e.offsetY };
    // console.log(`zoom = ${zoom}, pt = `, pt);
    _canvas.zoomToPoint(pt, zoom);
    event.e.preventDefault();
    event.e.stopPropagation();

    // 同步鼠标操作信息
    if (props._sync_ops && !event.hasOwnProperty('sync_tag')) {
        emit('syncMouseEvent', {
            method: 'onMouseWheel',
            event: event
        });
    }

    // 更新canvas的显示内容
    _canvas.requestRenderAll();
}

function onSelectionCleared(event) {
    // console.log('onSelectionCleared event = ', event);
    emit('unselectLabel');
}

function handleKeyDown(event) {
    // console.log('ImageRegion handleKeyDown event = ', event);
    LabelConfig.runShortCuts('ImageRegion', methods, event);
    for (let mode in _label_tools) {
        let tool = _label_tools[mode];
        _label_mode.value == mode && tool.handleKeyDown(event);
    }
}

function setCrossLinesVisibility(visible) {
    // console.log('setCrossLinesVisibility visible = ', visible);
    if (_horizontalLine) {
        _horizontalLine.set({ opacity: visible ? 1 : 0 });
    }
    if (_verticalLine) {
        _verticalLine.set({ opacity: visible ? 1 : 0 });
    }

    // 如果显示crossline，则取消鼠标显示
    if (visible) {
        _image.set({
            hoverCursor: 'none'
        });
    } else {
        _image.set({
            hoverCursor: 'crosshair'
        });
    }
    _canvas.requestRenderAll();
}

function emitMessage(msg) {
    emit(msg);
}

// 点击工具按钮或者快捷键，确保工具都是直接创建
function clickLabelTool(mode) {
    _label_result && (_label_result.editing_label_id = -1);
    changeLabelMode(mode);
}

// 设置状态，编辑和新增都会调用，所以增加了上面clickLabelTool
function changeLabelMode(mode) {
    // console.log('changeLabelMode mode = ', mode);
    // 判断mode是否在当前工具配置中，否则直接返回
    if (_tools_config.value.includes(mode) == false) {
        return;
    }

    // 保存当前工具的状态
    for (let mode in _label_tools) {
        let tool = _label_tools[mode];
        _label_mode.value == mode && tool.finish();
    }

    // 切换编辑模式
    if (mode == 'pointer') {
        setCrossLinesVisibility(false);
        _label_result && _label_result.setEditableMode(true);
    } else {
        setCrossLinesVisibility(true);
        _label_result && _label_result.setEditableMode(false);
    }

    _label_mode.value = mode;
}

function deleteLabel(label_id) {
    // console.log('ImageRegion deleteLabel label_id = ', label_id);
    // _label_result && _label_result.deleteLabel(label_id);
    emit('deleteLabel', label_id);
}

function selectLabel(label_id) {
    // console.log('ImageRegion selectLabel label_id = ', label_id);
    // _label_result && _label_result.selectLabel(label_id);
    emit('selectLabel', label_id);
}

// 窗口大小改变事件处理函数
function handleResize() {
    // console.log('imageRegion.vue handleResize');
    updateCanvasSize();
}

function autolabel() {
    pipelineDialog.value.showDialog({
        invoke_from: 'image',
        batch: false,
        input_data: {
            image: `file://${_image_path.value}`
        }
    });
}

async function onPipelineInput(data) {
    // console.log('ImageRegion.vue onPipelineInput data = ', data);
    // 创建pipeline的回调函数
    const jobId = generateTimestampBasedID();
    await API.stopAsyncPolling();
    API.asyncTaskPolling(`pipeline:${jobId}`, onPipelineOutput, 500, null, true);

    // 调用pipeline
    let result = await API.invoke('PipelineStart', {
        ...data,
        jobId: jobId,
        pairs_id: _pairs_id.value
    });
    if (result.code === 0) {
        if (!loading) {
            loading = ElLoading.service({
                lock: true,
                text: '自动标注中...',
                background: 'rgba(0, 0, 0, 0.7)'
            });
        } else {
            loading.setText('自动标注中...');
        }
    } else {
        ElMessage.error(result.msg);
    }
}

async function onPipelineOutput(jobId, message) {
    console.log(`onPipelineOutput jobId = ${jobId}, message = `, message);
    if (message.event === 'pipeline_result') {
        loading && loading.close();
        loading = null;
        _label_result.fromJson(message.data.labelResult);
        _label_result.bindCanvas(_canvas, _image, _label_tools);
        _label_result.label_panel.loadLabels(_label_result.labels);
        _label_result.label_panel.updateStatus();
        _label_result.saved = false;
    } else if (message.event === 'pipeline_error') {
        loading && loading.close();
        loading = null;
    }
}

// 加载图片区域的vue控件
onMounted(() => {
    initCanvas();
    updateCanvasSize(); // 设置初始尺寸
    window.addEventListener('resize', handleResize);
});

onUnmounted(() => {
    unsetCanvas();
    window.removeEventListener('resize', handleResize);
});
</script>

<style scoped>
.image-tools-wrapper {
    width: 100%;
    height: 100%;
    overflow: hidden;
    position: relative;
}

.image-toolbar-wrapper {
    position: absolute;
    left: 0px;
    top: 0px;
    width: 100%;
    z-index: 10;
}

.image-panel {
    width: 100%;
    position: relative;
}

.image-info {
    position: absolute;
    top: 10px;
    left: 10px;
    background-color: rgba(151, 151, 151, 0.7);
    color: rgb(255, 255, 255);
    padding: 5px 10px;
    border-radius: 4px;
    font-size: 14px;
    z-index: 1;
}
</style>
